/**********************************************************
 * This script provides syntax highlighting support for
 * the Ntriples format.
 * Ntriples format specification:
 *     http://www.w3.org/TR/rdf-testcases/#ntriples
 ***********************************************************/

/* 
 The following expression defines the defined ASF grammar transitions.

 pre_subject ->
 {
 ( writing_subject_uri | writing_bnode_uri )
 -> pre_predicate
 -> writing_predicate_uri
 -> pre_object
 -> writing_object_uri | writing_object_bnode |
 (
 writing_object_literal
 -> writing_literal_lang | writing_literal_type
 )
 -> post_object
 -> BEGIN
 } otherwise {
 -> ERROR
 }
 */
CodeMirror.defineMode("ntriples", function () {

    Location = {
        PRE_SUBJECT: 0,
        WRITING_SUB_URI: 1,
        WRITING_BNODE_URI: 2,
        PRE_PRED: 3,
        WRITING_PRED_URI: 4,
        PRE_OBJ: 5,
        WRITING_OBJ_URI: 6,
        WRITING_OBJ_BNODE: 7,
        WRITING_OBJ_LITERAL: 8,
        WRITING_LIT_LANG: 9,
        WRITING_LIT_TYPE: 10,
        POST_OBJ: 11,
        ERROR: 12
    };
    function transitState(currState, c) {
        var currLocation = currState.location;
        var ret;

        // Opening.
        if (currLocation == Location.PRE_SUBJECT && c == '<') ret = Location.WRITING_SUB_URI;
        else if (currLocation == Location.PRE_SUBJECT && c == '_') ret = Location.WRITING_BNODE_URI;
        else if (currLocation == Location.PRE_PRED && c == '<') ret = Location.WRITING_PRED_URI;
        else if (currLocation == Location.PRE_OBJ && c == '<') ret = Location.WRITING_OBJ_URI;
        else if (currLocation == Location.PRE_OBJ && c == '_') ret = Location.WRITING_OBJ_BNODE;
        else if (currLocation == Location.PRE_OBJ && c == '"') ret = Location.WRITING_OBJ_LITERAL;

        // Closing.
        else if (currLocation == Location.WRITING_SUB_URI && c == '>') ret = Location.PRE_PRED;
        else if (currLocation == Location.WRITING_BNODE_URI && c == ' ') ret = Location.PRE_PRED;
        else if (currLocation == Location.WRITING_PRED_URI && c == '>') ret = Location.PRE_OBJ;
        else if (currLocation == Location.WRITING_OBJ_URI && c == '>') ret = Location.POST_OBJ;
        else if (currLocation == Location.WRITING_OBJ_BNODE && c == ' ') ret = Location.POST_OBJ;
        else if (currLocation == Location.WRITING_OBJ_LITERAL && c == '"') ret = Location.POST_OBJ;
        else if (currLocation == Location.WRITING_LIT_LANG && c == ' ') ret = Location.POST_OBJ;
        else if (currLocation == Location.WRITING_LIT_TYPE && c == '>') ret = Location.POST_OBJ;

        // Closing typed and language literal.
        else if (currLocation == Location.WRITING_OBJ_LITERAL && c == '@') ret = Location.WRITING_LIT_LANG;
        else if (currLocation == Location.WRITING_OBJ_LITERAL && c == '^') ret = Location.WRITING_LIT_TYPE;

        // Spaces.
        else if (c == ' ' &&
            (
                currLocation == Location.PRE_SUBJECT ||
                currLocation == Location.PRE_PRED ||
                currLocation == Location.PRE_OBJ ||
                currLocation == Location.POST_OBJ
            )
        ) ret = currLocation;

        // Reset.
        else if (currLocation == Location.POST_OBJ && c == '.') ret = Location.PRE_SUBJECT;

        // Error
        else ret = Location.ERROR;

        currState.location = ret;
    }

    untilSpace = function (c) {
        return c != ' ';
    };
    untilEndURI = function (c) {
        return c != '>';
    };
    return {
        startState: function () {
            return {
                location: Location.PRE_SUBJECT,
                uris: [],
                anchors: [],
                bnodes: [],
                langs: [],
                types: []
            };
        },
        token: function (stream, state) {
            var ch = stream.next();
            if (ch == '<') {
                transitState(state, ch);
                var parsedURI = '';
                stream.eatWhile(function (c) {
                    if (c != '#' && c != '>') {
                        parsedURI += c;
                        return true;
                    }
                    return false;
                });
                state.uris.push(parsedURI);
                if (stream.match('#', false)) return 'variable';
                stream.next();
                transitState(state, '>');
                return 'variable';
            }
            if (ch == '#') {
                var parsedAnchor = '';
                stream.eatWhile(function (c) {
                    if (c != '>' && c != ' ') {
                        parsedAnchor += c;
                        return true;
                    }
                    return false
                });
                state.anchors.push(parsedAnchor);
                return 'variable-2';
            }
            if (ch == '>') {
                transitState(state, '>');
                return 'variable';
            }
            if (ch == '_') {
                transitState(state, ch);
                var parsedBNode = '';
                stream.eatWhile(function (c) {
                    if (c != ' ') {
                        parsedBNode += c;
                        return true;
                    }
                    return false;
                });
                state.bnodes.push(parsedBNode);
                stream.next();
                transitState(state, ' ');
                return 'builtin';
            }
            if (ch == '"') {
                transitState(state, ch);
                stream.eatWhile(function (c) {
                    return c != '"';
                });
                stream.next();
                if (stream.peek() != '@' && stream.peek() != '^') {
                    transitState(state, '"');
                }
                return 'string';
            }
            if (ch == '@') {
                transitState(state, '@');
                var parsedLang = '';
                stream.eatWhile(function (c) {
                    if (c != ' ') {
                        parsedLang += c;
                        return true;
                    }
                    return false;
                });
                state.langs.push(parsedLang);
                stream.next();
                transitState(state, ' ');
                return 'string-2';
            }
            if (ch == '^') {
                stream.next();
                transitState(state, '^');
                var parsedType = '';
                stream.eatWhile(function (c) {
                    if (c != '>') {
                        parsedType += c;
                        return true;
                    }
                    return false;
                });
                state.types.push(parsedType);
                stream.next();
                transitState(state, '>');
                return 'variable';
            }
            if (ch == ' ') {
                transitState(state, ch);
            }
            if (ch == '.') {
                transitState(state, ch);
            }
        }
    };
});

CodeMirror.defineMIME("text/n-triples", "ntriples");
